package com.bit.demo3;

//      注意:
//1、子类继承了父类，那么子类在构造的时候，需要先帮助父类来进行构造。需要再子类的构造方法当中，使用super关键字来显示的调用父类的构造方法。羽35626
//2、super和this的区别?重写和重载的区别?
//3、讲解了访问修饰限定符。
//      private 包访问权限 protected public
//      包访问权限:什么都不写就是默认包访问限权，只能在同一个包里面访问
//      protected:不同包，必须是子类才能访问。

//父类private修饰的成员变量是否被继承？
//没有被继承 下面有个A B的例子
//class A{
//    private int a = 10;
//    public int b = 9;
//}
//class B extends A{
//    public void fun(){
//        System.out.println(this.a);//报错
//        System.out.println(this.b);//没报错
//    }
//}

class Animal {
    public String name = "hello";
    public int age;
    protected int count;

    public Animal(String name,int age) {
        this.name = name;
        this.age = age;
    }
    public Animal(String name){
//        this.name = name;
    }


    public void eat() {
//        eat();//在构造方法中调用重写的方法(一个坑)
//        我们创建两个类, Animal 是父类, Dog 是子类. Dog 中重写 eat 方法. 并且在 B 的构造方法中调用 eat
//        执行结果是  name+"狼吞虎咽的吃"
//        1.构造 Dog 对象的同时, 会调用 Animal 的构造方法.
//        2.Animal 的构造方法中调用了 eat 方法, 此时会触发动态绑定, 会调用到 Dog 中的 eat
//        3.此时 Dog 对象自身还没有构造, 此时会调用 name+"狼吞虎咽的吃"
        System.out.println(name+"eat()");
    }
//    public Animal eat() {
//        System.out.println(name+"eat()");
//        System.out.println(count);
//        return null;
//    }//和下面Dog子类放回值为Dog构成重写     也构成协变类型  类型得是子类 或者相同
    public void eat(String food) {
        System.out.println(name + "吃" + food);
    }
}









class Dog extends Animal{
    public Dog(String name,int age) {
        super(name,age);
    }
//    动态绑定:     是多态的基础
//    有两个前提
//    1、父类引用引用子类的对象
//    2、通过这个父类引用调用父类和子类同名的覆盖方法。
//    同名的覆盖方法 也叫 重写:
//    1、方法名相同                                                1.static方法不可以重写
//    2、参数列表相同(个数+类型)                                注：  2.子类的访问修饰限定，要大于等于父类的访问修饰限定
//    3、返回值相同（有种特殊情况可能不一样19行和55行协变类型）            3.private修饰的方法   不能被重写
//    4、父子类的的情况下                                           4.被final修饰的方法   不能被重写

//
//    静态绑定（编译时绑定）：
//    根据你给定参数的类型＋个数 推导出你调用哪个函数
//    编译时绑定：通过函数的重载实现的。编译的时候，会根据你给的参数的个数和类型，在编译期，确定你最终调用的一个方法。



//public static void eat(){}错误    子类的访问限定修饰符一定要大于等于父类的访问限定修饰符(public protected default private)
    public void eat() {
        System.out.println(name+"狼吞虎咽的吃");
    }
//    public Dog eat() { //重写放回值可以不一样
//        System.out.println(name+"狼吞虎咽的吃");
//        return null;
//    }
}






class Bird extends Animal{
    public String wing;
    public String name = "world";

    public Bird(String name,int age,String wing) {
        super(name,age);
        this.wing = wing;
    }
    public Bird(String name) {
        super(name);
    }//父类要先构造

    public void fly() {
        System.out.println(super.name+"fly()"+age);
    }
}

//      父类引用  引用子类对象
//      有3中情况会发生向上转型
//      1.直接赋值
//      2.方法传参
//      3.方法返回



public class Test {
//    1.直接赋值
//    Dog dog = new Dog("haha",19);
//    Animal animal = dog;  //这两行等于下面那一行
    Animal animal = new Dog("haha",19);//向上转型

//    2.此时形参 animal 的类型是 Animal (基类), 实际上对应到 Bird (父类) 的实例.
    public static void feed(Animal animal) {
        animal.eat("谷子");
    }
    public static void main1(String[] args) {
        Bird bird = new Bird("圆圆",1,"black");
        feed(bird);
    }
//    3.此时方法 findMyAnimal 返回的是一个 Animal 类型的引用, 但是实际上对应到 Bird 的实例
    public static Animal findMyAnimal() {
        Bird bird = new Bird("圆圆",1,"black");
        return bird;
    }
    public static void main2(String[] args) {
        Animal animal = findMyAnimal();
    }

//    ========================================

    public static void main(String[] args) {
        Dog dog = new Dog("dog", 12);
        dog.eat();
        System.out.println("======================");
//        动态绑定
        Animal animal = new Dog("旺财", 23);
        animal.eat();//animal调用的eat()  为什么会打印 旺财狼吞虎咽的吃 41行左右
        Animal animal1 = new Bird("小飞侠");
        animal1.eat();

        System.out.println("=================");
//       重点： 通过父类引用 只能访问父类自己的成员或者方法 (注意)
        System.out.println(animal1.name);//将上面12行的this.name 注释掉 就打印hello了
//        System.out.println(animal1.wing);报错不可以访问wing  因为animal1的类型是animal类型
//        animal1.fly();也是同理 会报错 因为animal中没有fly方法 也没有wing属性

//        向下转型(不是很安全)
//        如果你想要通过animal1调用fl()方法 可以使用强制类型转换
//        Animal animal2 = new Bird("飞不起来");
//        Bird bird = (Bird)animal2;
//        bird.fly();//这样就可以调用了 但是不建议这样使用（可以理解为 不是所有的动物都是鸟）我们应该这样转Animal animal = new Dog("旺财",23);

//        Animal animal3 = new Dog("飞不起来");
//        Bird bird = (Bird)animal3;
//        bird.fly();//报错 类型转换异常 而且不安全 所以可以这样写
//        if(animal3 instanceof Bird){    //instanceof可以判定一个引用是否是某个类的实例. 如果是, 则返回 true. 这时再进行向下转型就比较安全了.
//        Bird bird = (Bird)animal3;
//        bird.fly()   //因为不是Bird类型 所以if语句进不去
//        }
    }

}